#include "bc_staticValue.hpp"
#include "meshTypes.hpp"
#include "field.hpp"
#include "boundaryPatch.hpp"

#include <iostream>

namespace hms
{

#define CHEAD(RETURN_TYPE) \
template<typename MeshType> \
RETURN_TYPE StaticValue<MeshType>::


CHEAD() StaticValue(
	Field<MeshType>& field, const BoundaryPatch& patch,
	bool isUniformArg
) :
	BaseType(Type::staticValue, field, patch)
{
	isUniform( isUniformArg );
}

CHEAD() StaticValue(
	Field<MeshType>& field, const BoundaryPatch& patch,
	const Ref<const ArrayXXs>& values, bool isUniformArg
) :
	StaticValue(field, patch, isUniformArg)
{
	set( values, isUniformArg );
}

/**
 * @brief Sets the boundary field at the provided patch to a uniform value.
 * 
 * @tparam MeshType 
 * @param field Field, whose boundary values at the specified patch are to be
 * set to the provided value.
 * @param patch Boundary patch at which to set the values.
 * @param value Uniform value to which the boundary field is set. Its number
 * of coefficients must be equal to the number of values per cell of the field.
 */
CHEAD(void) set(
	const Ref<const ArrayXXs>& values,
	bool isUniformArg
){
	assert( values.rows() == field().nComponents() );
	isUniform(isUniformArg);
	if ( isUniform() ){
		assert( values.cols() == 1 );
		field().ghostValues( patch() ).colwise() = values.reshaped();
	} else {
		if ( values.cols() != field().ghostValues( patch() ).cols() )
			std::cout
				<< "values.cols() = " << values.cols()
				<< ", field().ghostValues( patch() ).cols() = "
				<< field().ghostValues( patch() ).cols()
				<< "\nvalues:\n" << values
				<< '\n';
		assert( values.cols() == field().ghostValues( patch() ).cols() );
		field().ghostValues( patch() ) = values;
	}
}

/**
 * @brief Returns true if the boundary that was set most recently through the
 * object was set to a uniform value and false otherwise.
 * 
 * Use this to decide whether to call getUniform() or getList().
 */
CHEAD(bool) isUniform() const { return m_isUniform; }

CHEAD(void) isUniform( bool b ){
	m_isUniform = b;
}


/**
 * @brief Returns the value of the first ghost cell in the patch 
 * which the boundary condition belongs to.
 * 
 * @return Eigen::Ref<const ArrayXXs>
 */
CHEAD(auto) getUniform() const -> Eigen::Ref<const ArrayXXs>
{
	assert( m_isUniform && "Call getList instead!" );
	return field().values().col( patch().ghostCells()(0,0) );
}

CHEAD(auto) get() const -> Eigen::Ref<const ArrayXXs>
{
	if (m_isUniform){
		return getUniform();
	} else
		return getList();
}

/**
 * @brief Returns the values in the specified boundary field.
 * 
 * @return Eigen::Ref<const ArrayXXs>
 */
CHEAD(auto) getList() const -> Eigen::Ref<const ArrayXXs>
{
	assert( !m_isUniform && "Call getUniform instead!" );
	return field().ghostValues( patch() );
}

#undef CHEAD

#define INSTANTIATE_FOR_ALL_MESHTYPES(MeshType) \
template class StaticValue<MeshType>;
#include "meshInstantiator.hpp"


} // end namespace hms
